package edu.ruc.cluster.print;

import java.awt.Dimension;
import java.awt.Color;
import java.io.OutputStream;
import java.io.PrintStream;

import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.AbstractDocument;

@SuppressWarnings("serial")
public class ConsolePane extends JScrollPane {
	
		// Updated Version
	
	private JTextPane textPane = new JTextPane();
	
	private static ConsolePane console = null;
	
	public static synchronized ConsolePane getInstance(){
		if(console == null){
			console = new ConsolePane();
		}
		return console;
	}
	
	private ConsolePane(){
		
		setViewportView(textPane);
			// setup System.out
		PrintStream mySystemOut = new MyPrintStream(System.out, Color.BLACK);
		System.setOut(mySystemOut);
			// setup System.err
		PrintStream mySystemErr = new MyPrintStream(System.err, Color.RED);
		System.setErr(mySystemErr);
		
		textPane.setEditable(true);
		setPreferredSize(new Dimension(640, 120));
	}
	
	/** 
	 * @return: number of lines on textPane
	 */
	private final int getLineCount(){
		return textPane.getDocument().getDefaultRootElement().getElementCount();
	}
	
	/**
	 * Used: get the start offset of the specific line
	 * @param line
	 * @return
	 */
	private int getLineStartOffset(int line){
		Element lineElement = textPane.getDocument().getDefaultRootElement().getElement(line);
		if(lineElement == null)
			return -1;
		else
			return lineElement.getStartOffset();
	}
	
	/**
	 * get rid of the extra character when there's too many lines
	 * Replace the from start to end with str 
	 */
	public void replaceRange(String str, int start, int end){
		if(end < start){
			throw new IllegalArgumentException("end before start");
		}
		Document doc = textPane.getDocument();
		if(doc != null){
			try{
				if(doc instanceof AbstractDocument){
					((AbstractDocument)doc).replace(start, end-start, str, null);
				}else{
					doc.remove(start, end-start);
					doc.insertString(start, str, null);
				}
			}catch(BadLocationException e){
				throw new IllegalArgumentException(e.getMessage());
			}
		}
	}
	
	class MyPrintStream extends PrintStream{
		private Color foreground;
		
		MyPrintStream(OutputStream out, Color foreground){
			super(out, true);
			this.foreground = foreground;
		}
		
		public void write(byte[] buf, int off, int len){
			final String message = new String(buf, off, len);
			
			SwingUtilities.invokeLater(new Runnable(){
				public void run(){
					try{
						StyledDocument doc = (StyledDocument)textPane.getDocument();
						Style style = doc.addStyle("StyleName", null);
						StyleConstants.setForeground(style, foreground);
						StyleConstants.setFontSize(style, 14);
						StyleConstants.setFontFamily(style, "Consolas");
//						StyleConstants.setBold(style, true);
						doc.insertString(doc.getLength(), message, style);
						
					}catch(BadLocationException e){
						e.printStackTrace();
					}
					
					textPane.setCaretPosition(textPane.getDocument().getLength());
					
					int idealLine = 150;
					int maxExcess = 50;
					
					int excess = getLineCount() - idealLine;
					if(excess >= maxExcess){
						replaceRange("", 0, getLineStartOffset(excess));
					}
				}
			});
		}
	}
	/**	//old version
	private PipedInputStream piOut;
	private PipedInputStream piErr;
	private PipedOutputStream poOut;
	private PipedOutputStream poErr;
	
	private JTextPane textPane = new JTextPane();
	
	private static ConsolePane console = null;
	
	public static synchronized ConsolePane getInstance(){
		if(console == null){
			console = new ConsolePane();
		}
		return console;
	}
	
	private ConsolePane(){
		
		setViewportView(textPane);
		piOut = new PipedInputStream();
		piErr = new PipedInputStream();
		try{
			poOut = new PipedOutputStream(piOut);
			poErr = new PipedOutputStream(piErr);
		}catch(IOException e){
			e.printStackTrace();
		}
		
		System.setOut(new PrintStream(poOut, true));
		System.setErr(new PrintStream(poErr, true));
		textPane.setEditable(true);
		setPreferredSize(new Dimension(640, 120));
		
		new ReaderThread(piOut).start();
		new ReaderThread(piErr).start();
	}
	
	public final int getLineCount(){
		return textPane.getDocument().getDefaultRootElement().getElementCount();
	}
	
	public int getLineStartOffset(int line){
		Element lineElement = textPane.getDocument().getDefaultRootElement().getElement(line);
		if(lineElement == null)
			return -1;
		else
			return lineElement.getStartOffset();
	}
	
	public void replaceRange(String str, int start, int end){
		if(end < start){
			throw new IllegalArgumentException("end before start");
		}
		Document doc = textPane.getDocument();
		if(doc != null){
			try{
				if(doc instanceof AbstractDocument){
					((AbstractDocument)doc).replace(start, end-start, str, null);
				}else{
					doc.remove(start, end-start);
					doc.insertString(start, str, null);
				}
			}catch(BadLocationException e){
				throw new IllegalArgumentException(e.getMessage());
			}
		}
	}
	class ReaderThread extends Thread{
		PipedInputStream pi;
		ReaderThread(PipedInputStream pi){
			this.pi = pi;
		}
		public void run(){
			final byte[] buf = new byte[1024];
			while(true){
				try{
					final int len = pi.read(buf);
					if(len == -1)
						break;
					SwingUtilities.invokeLater(new Runnable(){
						public void run(){
							try{
								StyledDocument doc = (StyledDocument)textPane.getDocument();
								Style style = doc.addStyle("StyleName", null);
								Color foreground = pi == piOut ? Color.BLACK:Color.RED;
								StyleConstants.setForeground(style, foreground);
								
								String outstr = new String(buf, 0, len);
								doc.insertString(doc.getLength(), outstr, style);
							}catch(BadLocationException e){
								e.printStackTrace();
							}
							
							textPane.setCaretPosition(textPane.getDocument().getLength());
							
							int idealLine = 150;
							int maxExcess = 50;
							
							int excess = getLineCount() - idealLine;
							if(excess >= maxExcess){
								replaceRange("", 0, getLineStartOffset(excess));
							}
						}
					});
				}catch(IOException e){
//					e.printStackTrace();
				}
			}
		}
	}
	*/
}
